/**
 * 
 */
package edu.arizona.bio5.onto.util;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.openrdf.model.vocabulary.OWL;
import org.openrdf.model.vocabulary.XMLSchema;
import org.semanticweb.owlapi.io.OWLXMLOntologyFormat;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDisjointClassesAxiom;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.OWLSubDataPropertyOfAxiom;
import org.semanticweb.owlapi.model.OWLSubObjectPropertyOfAxiom;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

import edu.arizona.bio5.onto.modularity.Signature;

/**
 * Various utility methods
 * 
 * @author Pavel Klinov
 *
 */
public class OntologyUtils {
	
	@SuppressWarnings("rawtypes")
	public static final AxiomType[] INCOMPATIBLE_AXIOM_TYPES = {
		AxiomType.CLASS_ASSERTION,
		AxiomType.OBJECT_PROPERTY_ASSERTION,
		AxiomType.DATA_PROPERTY_ASSERTION,
		AxiomType.HAS_KEY,
		//AxiomType.ANNOTATION_PROPERTY_DOMAIN,
		//AxiomType.ANNOTATION_PROPERTY_RANGE,
		//AxiomType.SUB_ANNOTATION_PROPERTY_OF,
		AxiomType.DATATYPE_DEFINITION,
		AxiomType.NEGATIVE_DATA_PROPERTY_ASSERTION,
		AxiomType.NEGATIVE_OBJECT_PROPERTY_ASSERTION
	};
	
	public static void prepareForSyntacticModularity(OWLOntology ontology, OWLOntologyManager manager) {
		
		List<RemoveAxiom> removals = new ArrayList<RemoveAxiom>();
		
		for (AxiomType<OWLAxiom> type : INCOMPATIBLE_AXIOM_TYPES) {
			
			for (OWLAxiom axiom : ontology.getAxioms(type)) removals.add(new RemoveAxiom(ontology, axiom));
		}
		
		for (OWLAxiom axiom : ontology.getAxioms()) {
			//Removing non-logical stuff
			//if (!axiom.isLogicalAxiom()) removals.add(new RemoveAxiom(ontology, axiom));
			
			if (axiom.getAxiomType().equals(AxiomType.DISJOINT_CLASSES) && ((OWLDisjointClassesAxiom)axiom).getClassExpressions().size() == 1) removals.add(new RemoveAxiom(ontology, axiom));
			
			if (axiom.getAxiomType().equals(AxiomType.EQUIVALENT_CLASSES) && ((OWLEquivalentClassesAxiom)axiom).getClassExpressions().size() == 1) removals.add(new RemoveAxiom(ontology, axiom));
		}
		
		manager.applyChanges(removals);
	}
	
	public static void rewriteSugar(OWLOntology ontology, OWLOntologyManager manager) {
		
		OWLDataFactory factory = manager.getOWLDataFactory();
		List<RemoveAxiom> removals = new ArrayList<RemoveAxiom>();
		List<AddAxiom> adds = new ArrayList<AddAxiom>();
		
		for (OWLAxiom axiom : ontology.getAxioms(AxiomType.EQUIVALENT_CLASSES)) {
			
			List<OWLClassExpression> classList = ((OWLEquivalentClassesAxiom)axiom).getClassExpressionsAsList();
			
			for (int i = 0; i < classList.size() - 1; i++) {
				
				for (int j = i + 1; j < classList.size(); j++) {
					
					adds.add(new AddAxiom(ontology, factory.getOWLSubClassOfAxiom(classList.get(i), classList.get(j))));
					adds.add(new AddAxiom(ontology, factory.getOWLSubClassOfAxiom(classList.get(j), classList.get(i))));
				}
			}
			
			removals.add(new RemoveAxiom(ontology, axiom));
			
		}
		
		for (OWLAxiom axiom : ontology.getAxioms(AxiomType.DISJOINT_CLASSES)) {
			
			List<OWLClassExpression> classList = ((OWLDisjointClassesAxiom)axiom).getClassExpressionsAsList();
			
			for (int i = 0; i < classList.size() - 1; i++) {
				
				for (int j = i + 1; j < classList.size(); j++) {
					
					adds.add(new AddAxiom(ontology, factory.getOWLSubClassOfAxiom(classList.get(i), factory.getOWLObjectComplementOf(classList.get(j)))));
				}
			}
			
			removals.add(new RemoveAxiom(ontology, axiom));
			
		}		
		
		manager.applyChanges(adds);
		manager.applyChanges(removals);
	}
	
	/*
	 * Presumes every term is either a class (c) or an object property (op) or a data property (dp)
	 */
	public static OWLEntity termFromString(OWLDataFactory factory, String termString) {
		
		termString = termString.trim();
		
		OWLEntity term = null;
		int sepIndex = termString.indexOf(':');
		String prefix = termString.substring(0, sepIndex);
		String rest = termString.substring(sepIndex + 1);
		
		if ("c".equals(prefix)) {
			
			term = factory.getOWLClass(IRI.create(rest));
			
		} else if ("op".equals(prefix)) {
			
			term = factory.getOWLObjectProperty(IRI.create(rest));
			
		} else if ("dp".equals(prefix)) {
			
			term = factory.getOWLDataProperty(IRI.create(rest));
		}
		
		return term;
	}
	
	public static String termToString(OWLEntity term) {
		
		if (term.isOWLClass()) {
			
			return "c:" + term.toStringID();
			
		} else if (term.isOWLObjectProperty()) {
			
			return "op:" + term.toStringID();
			
		} else if (term.isOWLDataProperty()) {
			
			return "dp:" + term.toStringID();
		}
		
		return term.toStringID();
	}
	
	public static String termToURI(OWLEntity term) {
		String uri = term.toStringID();
		
		if (term.isOWLClass()) {		
			uri = OWL.CLASS + "/" + uri;
			
		} else if (term.isOWLObjectProperty()) {
			uri = OWL.OBJECTPROPERTY + "/" + uri;
			
		} else if (term.isOWLDataProperty()) {
			
			uri = OWL.DATATYPEPROPERTY + "/" + uri;
		}
		
		return uri;
	}
	
	public static OWLEntity termFromURI(OWLDataFactory factory, String termURI) {
		OWLEntity term = null;
		
		if (termURI.startsWith(OWL.CLASS.toString())) {
			
			term = factory.getOWLClass(IRI.create(termURI.substring(OWL.CLASS.toString().length() + 1)));
			
		} else if (termURI.startsWith(OWL.OBJECTPROPERTY.toString())) {
			
			term = factory.getOWLObjectProperty(IRI.create(termURI.substring(OWL.OBJECTPROPERTY.toString().length() + 1)));
			
		} else if (termURI.startsWith(OWL.DATATYPEPROPERTY.toString())) {
			
			term = factory.getOWLDataProperty(IRI.create(termURI.substring(OWL.DATATYPEPROPERTY.toString().length() + 1)));
		}
		
		return term;
	}
	
	public static void serializeAxioms(Set<OWLAxiom> axioms, OWLOntologyManager mgr, String fileName) throws OWLOntologyCreationException, OWLOntologyStorageException {
		
		File file = new File(fileName);
		OWLOntology ontology = mgr.createOntology(axioms, IRI.create(file.toURI()));
		
		mgr.saveOntology(ontology, new OWLXMLOntologyFormat(), IRI.create(file.toURI()));
	}

	public static boolean isIndexTerm(OWLEntity term) {

		return term.isOWLClass() || term.isOWLObjectProperty() || term.isOWLDataProperty();
	}
	
	public static Set<OWLEntity> signatureToOWLAPI(Signature sig, OWLOntology ontology, OWLDataFactory factory) {
		Set<OWLEntity> owlapiSig = new HashSet<OWLEntity>(sig.getURIs().size());
		
		for (URI termURI : sig) {
			for (OWLEntity ontoTerm : ontology.getEntitiesInSignature(IRI.create(termURI))) {
				owlapiSig.add(ontoTerm);
			}
		}
		
		return owlapiSig;
	}
	
	
	
	public static Set<String> extractNamespaces(OWLOntology ontology) {
		Set<String> result = new HashSet<String>();
		
		for (OWLAxiom axiom : ontology.getLogicalAxioms()) {
			
			for (OWLEntity definedTerm : getDefinedTerms(axiom)) {
				if (!definedTerm.getIRI().toString().startsWith(XMLSchema.NAMESPACE)
						&& !OWLRDFVocabulary.BUILT_IN_VOCABULARY_IRIS.contains(definedTerm.getIRI())) {
					
					result.add(getNamespace(definedTerm.getIRI().toURI()));
				}
			}
		}
	
		
		return result;
	}
	
	public static String getNamespace(URI term) {
		String uriString = term.toString();
		int index = Math.max(uriString.lastIndexOf('/'), uriString.lastIndexOf('#'));
		
		return index >= 0 ? uriString.substring(0,  index) : term.toString();
	}	

	/*
	 * TODO Think about it!
	 * Should return the list of terms which are "defined" by this axiom (for some meaning of defined)
	 * Right now we just look for TBox axioms
	 */
	public static Collection<OWLEntity> getDefinedTerms(OWLAxiom axiom) {
		List<OWLEntity> definedTerms = new ArrayList<OWLEntity>();
		
		if (axiom.getAxiomType().equals(AxiomType.SUBCLASS_OF)) {
			definedTerms.addAll(((OWLSubClassOfAxiom)axiom).getSubClass().getSignature());
		}
		else if (axiom.getAxiomType().equals(AxiomType.SUB_OBJECT_PROPERTY)) {
			definedTerms.addAll(((OWLSubObjectPropertyOfAxiom)axiom).getSubProperty().getObjectPropertiesInSignature());
		}
		else if (axiom.getAxiomType().equals(AxiomType.SUB_DATA_PROPERTY)) {
			definedTerms.addAll(((OWLSubDataPropertyOfAxiom)axiom).getSubProperty().getDataPropertiesInSignature());
		}
		else if (axiom.getAxiomType().equals(AxiomType.EQUIVALENT_CLASSES) || axiom.getAxiomType().equals(AxiomType.DISJOINT_CLASSES)){
			definedTerms.addAll(axiom.getSignature());
		}
		
		return definedTerms;
	}
}
